package com.nlelpct.manage.service.impl;

import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.GetObjectRequest;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectRequest;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.nlelpct.manage.config.OSSConfig;
import com.nlelpct.manage.service.IOssService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.util.Date;
import java.util.List;
import java.util.zip.Adler32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Service
public class OssServiceImpl implements IOssService {

//    @Value("${ossconfig.filePath}")
//    private String filePath;

    @Value("${ossconfig.bucketName}")
    private String bucketName;

    @Autowired
    private OSSConfig config;

    /**
     * Copyright @2018 nlelpct.
     *
     * @author: 黄磊
     * date:  2018/11/14  17:37
     * use to：上传对象第一种方式, 注意设置fileKey,可以使用UUID生成，注意加上后缀，不然下载后的类型不明确，没法双击打开。
     * modify：
     */
    @Override
    public String upload(String fileKey, File file) {
        fileKey = config.getFilePath() + fileKey;
        //1.建立客户端对象
        OSSClient ossClient = new OSSClient(config.getEndpoint(), config.getAccessKeyId(), config.getAccessKeySecret());
        String fileType = getFileType(fileKey);
        String url = null;
        try {
            //2.判断Bucket是否存在。不存在则尝试新建bucket
            if (ossClient.doesBucketExist(config.getBucketName())) {
            } else {
                // 创建新的Bucket
                ossClient.createBucket(config.getBucketName());
            }
            //创建上传Object的Metadata
            ObjectMetadata meta = new ObjectMetadata();
            //设置文件的类型
            meta.setContentType(getContentType(fileType));
            // 设置缓存行为
            meta.setCacheControl("no-cache");
            meta.setContentEncoding("utf-8");
            // 设置上传文件长度
            meta.setContentLength(file.length());
            // 设置上传MD5校验
//            String md5 = MD5Utils.getFileMD5String_old(file);
//            meta.setContentMD5(md5);
            //获取原文件名
            //设置文件上传时的请求头信息，这样上传返回文件存储的url路径，我们通过url路径直接下载就可以得到源文件名而不是加密的随机码。
            meta.setContentDisposition("attachment;filename=\"" + file.getName() + "\"");
            //设置上传请求
            PutObjectRequest request = new PutObjectRequest(config.getBucketName(), fileKey, file, meta);
            //3.上传文件
            ossClient.putObject(request);
            //上传成功再返回的文件路径
            url = config.getEndpoint().replaceFirst("http://", "http://" + config.getBucketName() + ".") + "/" + fileKey;
            //关闭流
            ossClient.shutdown();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {

        }
        return url;
    }

    /**
     * Copyright @2018 nlelpct.
     *
     * @author: 黄磊
     * date:  2018/11/14  17:36
     * use to：流方式上传对象，注意设置originName,代表原文件名，不然下载名字为uuid类型，但是不影响使用
     * modify：
     */
    @Override
    public String upload(String fileKey, String originName, InputStream inputStream) {
        fileKey = config.getFilePath() + fileKey;
        //1.建立客户端对象
        OSSClient ossClient = new OSSClient(config.getEndpoint(), config.getAccessKeyId(), config.getAccessKeySecret());
        String fileType = getFileType(fileKey);
        String url = null;
        try {
            //2.判断Bucket是否存在。不存在则尝试新建bucket
            if (ossClient.doesBucketExist(config.getBucketName())) {
            } else {
                // 创建新的Bucket
                ossClient.createBucket(config.getBucketName());
            }
            //创建上传Object的Metadata
            ObjectMetadata meta = new ObjectMetadata();
            //设置文件的类型
            meta.setContentType(getContentType(fileType));
            // 设置缓存行为
            meta.setCacheControl("no-cache");
            meta.setContentEncoding("utf-8");

            //获取原文件名
            //设置文件上传时的请求头信息，这样上传返回文件存储的url路径，我们通过url路径直接下载就可以得到源文件名而不是加密的随机码。
            meta.setContentDisposition("attachment;filename=\"" + originName + "\"");
            //设置上传请求
            PutObjectRequest request = new PutObjectRequest(config.getBucketName(), fileKey, inputStream, meta);
            //3.上传文件
            ossClient.putObject(request);
            //上传成功再返回的文件路径
            url = config.getEndpoint().replaceFirst("http://", "http://" + config.getBucketName() + ".") + "/" + fileKey;
            //关闭流
            ossClient.shutdown();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {

        }
        return url;
    }

    /**
     * Copyright @2018 nlelpct.
     *
     * @author: 黄磊
     * date:  2018/11/14  17:36
     * use to：删除对象
     * modify：
     */
    @Override
    public Boolean delete(String fileKey) {
        fileKey = config.getFilePath() + fileKey;
        //判断是否存在，不存在返回null；
        Boolean fileExist = isFileExist(fileKey);
        if (!fileExist) {
            return true;
        }
        //1.建立客户端对象
        OSSClient ossClient = new OSSClient(config.getEndpoint(), config.getAccessKeyId(), config.getAccessKeySecret());

        try {
            //删除文件
            ossClient.deleteObject(config.getBucketName(), fileKey);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            //关闭流
            ossClient.shutdown();
        }
        return true;
    }

    /**
     * Copyright @2018 nlelpct.
     *
     * @author: 黄磊
     * date:  2018/11/14  17:35
     * use to：下载文件链接（有效时间自定义，expire以 分钟 来设置）
     * modify：
     */
    @Override
    public String download(String fileKey, Long expire) {
        fileKey = config.getFilePath() + fileKey;
        return getUrlLinkToDownload(fileKey, expire);
    }

    /**
     * 时    间: 2018/12/29 14:48
     * 方法描述：文件下载，通过服务器先从oss下载到服务器，然后打包转发到前端
     * 返回类型：
     * 修改内容：（若修改了请注明修改人，修改时间，修改内容）
     */
    @Override
    public void download(String fileKey, HttpServletRequest request, HttpServletResponse response) throws IOException {

        String endpoint = config.getEndpoint();
        String accessKeyId = config.getAccessKeyId();
        String accessKeySecret = config.getAccessKeySecret();
        String bucketName = config.getBucketName();
        // 初始化
        OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
        ;
        OSSObject ossObject = ossClient.getObject(bucketName, config.getFilePath() + fileKey);
        InputStream inputStream = ossObject.getObjectContent();

        BufferedInputStream buff = new BufferedInputStream(inputStream);
        BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
        int len = -1;
        while ((len = buff.read()) != -1) {
            out.write(len);
        }
        buff.close();
        out.close();
        ossClient.shutdown();
    }


    /**
     * 时    间: 2018/12/29 14:48
     * 方法描述：文件下载，返回zip流文件
     * 返回类型：
     * 修改内容：（若修改了请注明修改人，修改时间，修改内容）
     */
    @Override
    public File download(List<String> fileKeys) throws IOException {

        String endpoint = config.getEndpoint();
        String accessKeyId = config.getAccessKeyId();
        String accessKeySecret = config.getAccessKeySecret();
        String bucketName = config.getBucketName();
        // 初始化
        OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
        ;
        // 创建临时文件
        File zipFile = File.createTempFile("tempory", ".zip");
        FileOutputStream f = new FileOutputStream(zipFile);
        CheckedOutputStream csum = new CheckedOutputStream(f, new Adler32());
        ZipOutputStream zos = new ZipOutputStream(csum);
        for (String fileKey : fileKeys) {
            OSSObject ossObject = ossClient.getObject(bucketName, config.getFilePath() + fileKey);
            InputStream inputStream = ossObject.getObjectContent();
            int bytesRead = 0;
            zos.putNextEntry(new ZipEntry(fileKey));
            while ((bytesRead = inputStream.read()) != -1) {
                zos.write(bytesRead);
            }
            inputStream.close();
            zos.closeEntry();
        }
        zos.close();
        ossClient.shutdown();
        return zipFile;
    }

    /**
     * Copyright @2018 nlelpct.
     *
     * @author: 黄磊
     * date:  2018/11/14  17:35
     * use to：下载文件链接（链接有效时间1小时）
     * modify：
     */
    @Override
    public String download(String fileKey) {
        fileKey = config.getFilePath() + fileKey;
        return getUrlLinkToDownload(fileKey, null);
    }


    //获取下载链接抽取方法
    @Override
    public String getUrlLinkToDownload(String fileKey, Long expire) {

        //1.建立客户端对象
        OSSClient ossClient = new OSSClient(config.getEndpoint(), config.getAccessKeyId(), config.getAccessKeySecret());
        String downloadLink = null;
        try {


            //判断是否存在
            boolean exist = ossClient.doesObjectExist(config.getBucketName(), fileKey);
            if (!exist) {
                return null;
            }
            // 设置过期时间
            Date date = null;
            if (expire != null && expire != 0) {
                date = new Date(System.currentTimeMillis() + 1000 * 60 * expire);
            } else {
                //默认1个小时,1个小时有效！
                date = new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 1l);
            }
            URL url = ossClient.generatePresignedUrl(config.getBucketName(), fileKey, date, HttpMethod.GET);
            downloadLink = url.toString();
            downloadLink = downloadLink.replace("-internal", "");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭流
            ossClient.shutdown();
        }
        return downloadLink;
    }

    /**
     * Copyright @2018 nlelpct.
     *
     * @author: 黄磊
     * date:  2018/11/14  17:34
     * use to：完整文件名获取文件后缀,不包含“.”
     * modify：
     */
    public static String getFileType(String fileName) {
        return fileName.substring(fileName.lastIndexOf(".") + 1);
    }


    /**
     * Copyright @2018 nlelpct.
     *
     * @author: 黄磊
     * date:  2018/11/14  17:34
     * use to：完整文件名获取文件名字
     * modify：
     */
    public static String getFileNamePre(String fileName) {
        return fileName.substring(0, fileName.lastIndexOf("."));
    }

    /**
     * Copyright @2018 nlelpct.
     *
     * @author: 黄磊
     * date:  2018/11/14  17:34
     * use to：文件类型格式化
     * modify：
     */
    private static String getContentType(String fileType) {
        fileType = fileType.toLowerCase();
        String contentType = "";
        switch (fileType) {
            case "bmp":
                contentType = "image/bmp";
                break;
            case "gif":
                contentType = "image/gif";
                break;
            case "png":
            case "jpeg":
            case "jpg":
                contentType = "image/jpeg";
                break;
            case "html":
                contentType = "text/html";
                break;
            case "txt":
                contentType = "text/txt";
                break;
            case "xls":
                contentType = "application/excel";
                break;
            case "ppt":
            case "pptx":
                contentType = "application/powerpoint";
                break;
            case "doc":
            case "docx":
                contentType = "application/word";
                break;
            case "xml":
                contentType = "text/xml";
                break;
            case "mp4":
                contentType = "video/mp4";
                break;
            case "avi":
                contentType = "video/avi";
                break;
            case "flv":
                contentType = "video/flv";
                break;
            case "3gp":
                contentType = "video/3gp";
                break;
            case "mov":
                contentType = "video/mov";
                break;
            default:
                contentType = "application/stream";
                break;
        }
        return contentType;
    }


    /**
     * Copyright @2018 nlelpct.
     *
     * @author: 黄磊
     * date:  2018/11/14  17:33
     * use to：判断对象是否存在
     * modify：
     */
    @Override
    public Boolean isFileExist(String fileKey) {
        fileKey = config.getFilePath() + fileKey;
        //1.建立客户端对象
        OSSClient ossClient = new OSSClient(config.getEndpoint(), config.getAccessKeyId(), config.getAccessKeySecret());
        Boolean isExist = false;
        try {

            isExist = ossClient.doesObjectExist(config.getBucketName(), fileKey);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭流
            ossClient.shutdown();
        }
        return isExist;

    }

    public ObjectMetadata getFileInfo(OSSConfig config, String fileKey) {
        fileKey = config.getFilePath() + fileKey;

        //1.建立客户端对象
        OSSClient ossClient = new OSSClient(config.getEndpoint(), config.getAccessKeyId(), config.getAccessKeySecret());
        Boolean isExist = false;
        ObjectMetadata meta = null;
        try {

            meta = ossClient.getObjectMetadata(config.getBucketName(), fileKey);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭流
            ossClient.shutdown();
        }
        return meta;
    }


    @Override
    public String downLoadFile(String fileKey, String localFilePath) {
        // log.info("==>>downLoadFile fikeKey:{}",fileKey);
        if (!isFileExist(fileKey)) {
            return "";
        }
        String newFileKey = config.getFilePath() + fileKey;
        OSSClient ossClient = new OSSClient(config.getEndpoint(), config.getAccessKeyId(), config.getAccessKeySecret());
        ossClient.getObject(new GetObjectRequest(bucketName, newFileKey), new File(localFilePath));
        return localFilePath;
    }

    @Override
    public String uploadToOSS(File sourceFile, boolean isDeleteSourceFile, String fileKey) {
        if (!sourceFile.exists() || StringUtils.isBlank(fileKey)) {
            return "";
        }
        try {
            OSSClient ossClient = new OSSClient(config.getEndpoint(), config.getAccessKeyId(), config.getAccessKeySecret());

            if (!ossClient.doesBucketExist(bucketName)) {
                ossClient.createBucket(bucketName);
            }

            String newFileKey = config.getFilePath() + fileKey;
            PutObjectRequest request = new PutObjectRequest(bucketName, newFileKey, sourceFile);
            ossClient.putObject(request);
        } catch (Exception e) {
            if (isDeleteSourceFile && sourceFile.exists()) {
                sourceFile.delete();
            }
            //  log.info("==>>uploadToOSS File found Exception:{}", ExceptionUtils.getInfo02(e));
//            throw new GlobalException(CodeMsg.OSS_ERROR);
        } finally {
            //ossClient.shutdown();
            if (isDeleteSourceFile) {
                if (sourceFile.exists()) {
                    sourceFile.delete();//会删除源文件
                }
            }
        }
        return fileKey;
    }

}
